package com.jmxcfc.blfsc.huisheng.service;

import com.jmxcfc.blfsc.huisheng.exception.HsSecurityException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;


/**
 * 签名工具类
 * @author zhuyiping
 */
@Slf4j
@Service
public class SignatureService {


    /**
     * 秘钥由运营提供(企业信息中的appSecret)
     */
    @Value("${huisheng.privateKey}")
    private String privateKeyStr;


    /**
     * 公钥字符字符串
     */
    @Value("${huisheng.publicKey}")
    private String publicKeyStr;


    /**
     * 数字签名算法
     */
    private static final String SHA1_256_RSA = "SHA256WithRSA";




    /**
     * 私钥：签名、解密
     */
    private PrivateKey privateKey;


    /**
     * 公钥:验签、加密【暂时无用到公钥】
     */
    private PublicKey publicKey;


    /**
     * 初始化公钥私钥
     */
    @PostConstruct
    public void init() {
        log.info("初始化上上签私钥--开始");
        initProvider();
        initPrivateKey();
        initPublicKey();
        log.info("初始化上上签私钥--结束");
    }

    /**
     * 注册Provider
     */
    private void initProvider() {
        try {
            Security.addProvider(new BouncyCastleProvider());
        } catch (Exception e) {
            throw new HsSecurityException(e.getMessage(), e);
        }
    }

    /**
     * 初始化PrivateKey(应用私钥,JMX方生成),用于加签
     */
    private void initPrivateKey() {
        if (StringUtils.isBlank(privateKeyStr)) {
            log.error("慧声初始化私钥:私钥字符串不存在,为空");
            throw new HsSecurityException("慧声初始化私钥:私钥字符串不存在");
        }
        try {
            this.privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr)));
            if (this.privateKey == null) {
                throw new HsSecurityException("慧声初始化私钥:获取私钥失败");
            }
            log.info("慧声初始化私钥成功");
        } catch (Exception e) {
            throw new HsSecurityException(e.getMessage(), e);
        }
    }


    private void initPublicKey() {
        if (StringUtils.isBlank(publicKeyStr)) {
            log.error("慧声初始化公钥:公钥字符串不存在,为空");
            throw new HsSecurityException("慧声初始化公钥:公钥字符串不存在");
        }
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            this.publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr)));
            if (publicKey == null) {
                log.error("慧声初始化公钥:获取公钥失败");
                throw new HsSecurityException("慧声初始化公钥:获取公钥失败");
            }
            log.info("慧声初始化公钥成功");
        } catch (Exception e) {
            throw new HsSecurityException(e.getMessage(), e);
        }
    }


    /**
     * 加签字符串进行升序后拼接 key=value&key=value
     * @param paramMap
     * @return
     */
    public  String getSignContent(Map<String, String> paramMap) {
        StringBuilder requestParamSb = new StringBuilder();
        // 排序后，按照  key1=value1&key2=value2
        paramMap.entrySet().stream().sorted(Map.Entry.comparingByKey())
                .forEach(entry -> requestParamSb.append(entry.getKey()).append("=").append(entry.getValue().trim()).append("&"));
        return requestParamSb.deleteCharAt(requestParamSb.length() - 1).toString();
    }


    /**
     * 加签
     * @param content 需要加签的字符串
     * @return
     * @throws Exception
     */
    public  String rsa256Sign(String content) throws Exception {
        Signature signature = Signature.getInstance(SHA1_256_RSA);
        signature.initSign(this.privateKey);
        //UTF_8
        signature.update(content.getBytes(StandardCharsets.UTF_8));
        byte[] signed = signature.sign();
        return new String(Base64.encodeBase64(signed));
    }


    /**
     * 验签
     * @param content
     * @param sign
     * @return
     * @throws Exception
     */
    public  boolean rsa256CheckContent(String content, String sign) throws Exception {
        Signature signature = Signature.getInstance(SHA1_256_RSA);
        signature.initVerify(this.publicKey);
        //UTF_8
        signature.update(content.getBytes(StandardCharsets.UTF_8));
        return signature.verify(Base64.decodeBase64(sign.getBytes()));
    }


}